home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Languguage OS 2
/
Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO
/
language
/
awe
/
awe-full.lha
/
Awe2
/
DoNotUseThisSrc
/
MultiSimMux.cc
< prev
next >
Wrap
C/C++ Source or Header
|
1990-08-08
|
8KB
|
364 lines
// This may look like C code, but it is really -*- C++ -*-
//
// Copyright (C) 1988 University of Illinois, Urbana, Illinois
//
// written by Dirk Grunwald (grunwald@cs.uiuc.edu)
//
#ifdef __GNUG__
# pragma implementation
#endif
#include "MultiSimMux.h"
#include "SpinLock.h"
#include "SpinBarrier.h"
#include "SpinFetchAndOp.h"
#include "AwesimeHeap.h"
#include "Thread.h"
#include "TimeSchedulerSplayPQ.h"
#include "ReserveByException.h"
#include <math.h>
static SpinBarrier CpuBarrier(0);
//
// The PendingEvents queue is private to each processor
//
static TimeSchedulerPQ *PendingEvents[MaxCpuMultiplexors];
static SpinLock PendingEventsLock[MaxCpuMultiplexors];
extern int CpuMuxDebugFlag;
MultiSimMux::MultiSimMux(int debug, int spinMax)
: SimulationMultiplexor(debug), MultiCpuMux(debug)
{
ThisSimulationMultiplexor = this;
ThisCpu = this;
CpuMuxDebugFlag = debug;
pNameTemplate = "SimMux";
cpuBarrier = &CpuBarrier;
CpuBarrier.maxLoops(spinMax);
sprintf(nameSpace, "[%s-%d] ", pNameTemplate, MultiCpuMux::iYam);
pName = nameSpace;
MultiCpuMux::allocateLocalEventStructures(0,1);
MultiSimMux::allocateLocalEventStructures(0,1);
}
void
MultiSimMux::allocateLocalEventStructures(int newIYam, int outOf)
{
#ifndef NDEBUG
if (CpuMuxDebugFlag) {
CpuCerrLock.reserve();
cerr << name() << "Allocate SimMux structures for " << newIYam << "\n";
CpuCerrLock.release();
}
#endif /* NDEBUG */
PendingEvents[newIYam] = new TimeSchedulerSplayPQ;
myPendingEvents = PendingEvents[newIYam];
myPendingEventsLock = &PendingEventsLock[newIYam];
#ifndef NDEBUG
if (CpuMuxDebugFlag) {
CpuCerrLock.reserve();
cerr << name() << "set CpuBarrier height to " << outOf << "\n";
CpuCerrLock.release();
}
#endif /* NDEBUG */
CpuBarrier.height( outOf );
}
void
MultiSimMux::allocateEventStructures(int newIYam, int outOf)
{
CpuCerrLock.reserve();
cerr << name() << " enter allocateEventStructures, call local\n";
CpuCerrLock.release();
allocateLocalEventStructures(newIYam, outOf);
//
// must do this after the local structures get set up so that
// CpuMultiplexors does not increase until all data structures are
// in place, otherwise people may poke at them before theyre set up.
//
// CpuMultiplexor::allocateEventStructures(newIYam, outOf);
}
void
MultiSimMux::deallocateEventStructures()
{
// CpuMultiplexor::deallocateEventStructures();
#ifndef NDEBUG
if (CpuMuxDebugFlag) {
CpuCerrLock.reserve();
cerr << name() << "Deallocate SimMux structures for" << MultiCpuMux::iYam << "\n";
CpuCerrLock.release();
}
#endif /* NDEBUG */
PendingEventsLock[0].reserve();
while ( ! myPendingEvents -> empty() ) {
assert( MultiCpuMux::iYam != 0 );
TimeScheduler& item = myPendingEvents -> front();
PendingEvents[0] -> enq( item );
myPendingEvents -> del_front();
}
PendingEventsLock[0].release();
delete myPendingEvents;
myPendingEvents = 0;
PendingEvents[ MultiCpuMux::iYam ] = 0;
#ifndef NDEBUG
if (CpuMuxDebugFlag) {
CpuCerrLock.reserve();
cerr << name() << "set CpuBarrier height to " << CpuMultiplexors << "\n";
CpuCerrLock.release();
}
#endif /* NDEBUG */
CpuBarrier.height(CpuMultiplexors);
}
void
MultiSimMux::fireItUp( int cpus, unsigned shared )
{
#ifndef NDEBUG
if (CpuMuxDebugFlag) {
CpuCerrLock.reserve();
cerr << name() << "Allocate " << shared << " bytes of shared memory\n";
CpuCerrLock.release();
}
#endif /* NDEBUG */
if ( cpus > 1 ) {
extern void SharedMemoryInit( unsigned );
SharedMemoryInit( shared );
}
cerr << "in MultiSimMux::fireItUp, call warm\n";
warmThePot( cpus );
cerr << "in MultiSimMux::fireItUp, call stir\n";
stirItAround();
cerr << "in MultiSimMux::fireItUp, call cool\n";
coolItDown();
}
void
MultiSimMux::warmThePot(int cpus)
{
if ( cpus > MaxCpuMultiplexors ) {
cpus = MaxCpuMultiplexors;
}
CpuBarrier.height(cpus);
// multiCpu.warmThePot(cpus);
MultiCpuMux::warmThePot(cpus);
int ok = CpuBarrier.rendezvous();
#ifndef NDEBUG
if (! ok ) {
CpuCerrLock.reserve();
cerr << name() << "barrier overrun\n";
CpuCerrLock.release();
}
#endif
}
void
MultiSimMux::stirItAround()
{
while( ! *(MultiCpuMux::terminated) ) {
MultiCpuMux::stirItAround();
if (! *(MultiCpuMux::terminated) ) {
int tasks = advanceTime();
#ifndef NDEBUG
if ( CpuMuxDebugFlag && tasks < 1 ) {
CpuCerrLock.reserve();
cerr << name() << "nothing to do, hang out\n";
CpuCerrLock.release();
#endif
}
}
}
}
void
MultiSimMux::coolItDown()
{
CpuBarrier.rendezvous();
MultiCpuMux::coolItDown();
//
// In case we call rendezvous again
//
CpuBarrier.height(CpuMultiplexors);
}
//
// Advance time. Assumes all other CPUs are idle, so no locking
// on event structures is needed.
//
int
MultiSimMux::advanceTime()
{
//
// If, for some reason, we fail to rendezvous after the timeout,
// click on debug output.
//
int ok = CpuBarrier.rendezvous();
#ifndef NDEBUG
if ( !ok ) {
int oldFlag = CpuMuxDebugFlag;
CpuMuxDebugFlag = 1;
CpuCerrLock.reserve();
cerr << name() << "rendezvous fails, enable debugging\n";
CpuCerrLock.release();
while ( ! CpuBarrier.rendezvous() );
CpuMuxDebugFlag = oldFlag;
CpuCerrLock.reserve();
cerr << name() << "rendezvous resumes, reset debugging\n";
CpuCerrLock.release();
}
#endif /* NDEBUG */
//
// Nothing is locked at this point.
//
if ((MultiCpuMux::iYam) == 0) {
#ifndef NDEBUG
if (CpuMuxDebugFlag) {
CpuCerrLock.reserve();
cerr << name() << "Scan " << CpuMultiplexors << " for pending\n";
CpuCerrLock.release();
}
#endif /* NDEBUG */
//
// Find the minimum time over all pending event piles
//
double when = MAXFLOAT;
bool validItem = FALSE;
for (int i = 0; i < CpuMultiplexors; i++ ) {
TimeSchedulerPQ *p = PendingEvents[i];
if ( ! p -> empty() ) {
double hapAt = (p -> front()).time();
if (hapAt < when) {
when = hapAt;
validItem = TRUE;
}
#ifndef NDEBUG
if (CpuMuxDebugFlag) {
CpuCerrLock.reserve();
cerr << name() << i;
cerr << " has one at " << hapAt << "\n";
CpuCerrLock.release();
}
#endif /* NDEBUG */
}
#ifndef NDEBUG
else {
if (CpuMuxDebugFlag) {
CpuCerrLock.reserve();
cerr << name() << i << " has nothing\n";
CpuCerrLock.release();
}
}
#endif /* NDEBUG */
}
if ( !validItem ) {
#ifndef NDEBUG
if (CpuMuxDebugFlag) {
CpuCerrLock.reserve();
cerr << name() << " Unable to advance time, exit \n";
CpuCerrLock.release();
}
#endif /* NDEBUG */
*(MultiCpuMux::terminated) = 1;
}
else {
#ifndef NDEBUG
if (CpuMuxDebugFlag) {
CpuCerrLock.reserve();
cerr << name() << " ADVANCE TIME TO " << when << "\n" ;
CpuCerrLock.release();
}
#endif /* NDEBUG */
assert( CurrentSimulatedTime <= when );
CurrentSimulatedTime = when;
}
}
CpuBarrier.rendezvous();
//
// Now that we know the time of the minimum entry in the
// heap, remove all the events which occur at that time
// or before that time (actually, that is an error).
//
// Each process does its own work to reduce overhead.
// We keep track of how many processes were added to
// the current events queue and then bump the global
// events count by that amout.
//
// This cannot cause race conditions because we only
// use this info in the rendevzous to advance time
// code above, and not all cpus will be there yet
// (i.e. the current cpu is not there)
//
int added = 0;
MultiCpuMux::myCurrentEventsLock -> reserve();
TimeSchedulerPQ *p = myPendingEvents;
while ( ! p -> empty() ) {
TimeScheduler &item = p -> front();
if ( item.time() > CurrentSimulatedTime )
break;
MultiCpuMux::addUnlocked( item.thread() );
added++;
p -> del_front();
}
*(MultiCpuMux::myCurrentEventsCounter) += added;
MultiCpuMux::myCurrentEventsLock -> release();
MultiCpuMux::globalCurrentEventsCounter -> add(added);
#ifndef NDEBUG
if ( CpuMuxDebugFlag ) {
CpuCerrLock.reserve();
cerr << name() << " added " << added << " threads to current";
cerr << ", leaving me " << myPendingEvents -> length() << "\n";
CpuCerrLock.release();
}
#endif
return(added);
}